# coding=gbk
"""ʵ������ʱ��Ҫ��ģ��"""
import os
import time
import math
from datetime import datetime
import logging
import json
from typing import Union

from pyalgotrade.broker import backtesting
from pyalgotrade.bar import Bars
from pyalgotrade.barfeed import csvfeed
from pyalgotrade.bar import Frequency
from tqsdk import TqApi
from tqsdk.algorithm import Twap
import pandas as pd

from enum_var import Action

pd.set_option('display.max_columns', None)  # dataframe ��רΪ�ַ�����ʱ��Ҫ��ʡ�Ժ�
pd.set_option('display.width', 1000)  # ��ӡ��һ�еĿ���Ϊ1000�� ������Ϊ���õĲ����������С�
logger = logging.getLogger('Yhlz')

"""----------------------------------------------���ߺ���---------------------------------------------"""


def KQ2ts(symbol: str) -> str:
    """ ���ں�Լתtushare"""
    temp = symbol.split('.')
    if 'SHFE' in temp[0]:
        return temp[1].upper() + '.' + 'SHF'
    elif 'CFFEX' in temp[0]:
        return temp[1].upper() + '.' + 'CFX'
    elif 'DCE' in temp[0]:
        return temp[1].upper() + '.' + temp[0]
    elif 'CZCE' in temp[0]:
        tenYear = str(datetime.now().year)[2]
        temp2 = []
        for i, item in enumerate(temp[1]):
            if item.isdigit():
                temp2 = [temp[1][:i], temp[1][i:]]
                break
        return temp2[0].upper() + tenYear +  temp2[1] + '.' + 'ZCE'
        # TODO ֣�����Ĵ���û��ʮ����������2105��1105��ͬһ����Լ���֣�������Ҫ��ȡʱ���ж�һ��,���ǵ���2029�껹�ǻ����910��001֮��������ͬʮ����ݵ����⡣
    elif 'INE' in temp[0]:
        return temp[1].upper() + '.' + temp[0]
    else:
        assert False, f'δ֪�Ľ��������ͣ�{temp[0]} ���飡'


def ts2KQ(symbol: str) -> str:
    """ tushareת����"""
    temp = symbol.split('.')
    if 'SHF' in temp[1]:
        return 'SHFE' + '.' + temp[0].lower()
    elif 'CFX' in temp[1]:
        return 'CFFEX' + '.' + temp[0]
    elif 'DCE' in temp[1]:
        return temp[1] + '.' + temp[0].lower()
    elif 'ZCE' in temp[1]:
        for i in temp[0]:
            if i.isdigit():
                temp[0] = ''.join(temp[0].split(i, 1))  # AP2107.ZCE
                # split()�ڶ���������ʾ�ֶ��ٶΣ�Ĭ�Ϸ����У����� ����� AP22xx
                # ���ֵ�ʱ�����-> ['AP', 'xx'],���ڶ�������ָ��1����ᱣ��22�ĵڶ���2
                break
        return 'CZCE' + '.' + temp[0]
    elif 'INE' in temp[1]:
        return temp[1] + '.' + temp[0].lower()


class RealBroker(backtesting.Broker):
    """
    �̳�pyalgotrade�Ļ��࣬��ʵ�ֺ�tqsdk����
    ����ʵ�ָ��������ļ���������ͬƷ����ƽ����ƽ��֮������顣
    """

    def __init__(self, api: 'TqApi', context, position=None):
        """
        :param strategy: �������в������Ƶ�list
        strategy��alltick��Ҫһ�𴫣������ǵ���û�У���Ҫ���¶�ȡ
        """
        feed = RealFeed()
        super().__init__(10000, feed)
        self.api = api
        self.accountInfo = self.api.get_account()
        if position:
            self.posDict = position
        else:
            self.posDict = self.api.get_position()
        
        self.context = context
        self.orderQueue = []
        self.unfilledQueue = []
        self.cancelOrderQueue = []
        self.reInsertQueue = []
        self.strategys = {}
        self.strategyNow = None  # �������ʱ��onbars���е�strategy����RealBroker��ʵ���п����޸����ֵ��������ʾ�ڲ���ʱ���еĲ���
        self.cash = 0
        self.balence = 0
        self.strategyAccount = {}  # �洢һ������ķֲ��Ե��˻���Ϣ
        self.allMainTick = {}
        # TODO ��ô������15���������������δ�ɽ������ٴο��е�ʱ�򻹻�������ڣ���ô��Ҫ�����������������cancelorderqueue���Զ����¡�
        #  ���������ֱ����tqsdk������ô�ֻ���δ���̵������Ҫ�жϣ���ʼ��ʱ��Ȼû���̡�

        # highlight ��ȡ֮ǰδ�ɽ������������ɶ������µ���
        # highlight �߼����ӣ���ʱ�ֹ�������


    def setAllMainTick(self, allMainTick):
        self.allMainTick = allMainTick

    def setAllTick(self, allTick):
        self.allTick = allTick

    def setStrategys(self, strategys):
        self.strategys =strategys

    def creatAccountHelp(self):
        """���ڳ�ʼ��strategy��Ҫbroker�����Ը���strategy������account help ֻ���Ժ����"""
        realAccount = pd.read_csv(self.context.current_account_path)
        for item in self.strategys:
            self.strategyAccount[item] = _virtualAccountHelp(realAccount[realAccount.loc[:, 'strategy'] == item])

    def getInstrumentTraits(self, instrument):
        pass

    def getCash(self, includeShort=True, strategyName=None):
        """
        Returns the available cash.
        ��ʵ���ڻ������ȡ���Ƕ�̬Ȩ��
        :param includeShort: Include cash from short positions.
        :type includeShort: boolean.
        """
        logger.debug('�ֽ���' + str(self.strategyAccount[strategyName].getCash()))
        return self.strategyAccount[strategyName].getCash()

    def getShares(self, instrument, strategyName=None):
        """����һ�����ֲ֣���ͷΪ����"""
        # logger.debug(f'getshare1:{time.time()}')
        if strategyName == None:
            strategyName = type(self.strategyNow).__name__  # ��ȡʵ��������
        temp = self.getPositions(strategyName)
        # logger.debug(f'getshare2:{time.time()}')
        # logger.debug(f'getshare��ȡ���ĳֲ��ǣ�{temp}')  # test
        temp = temp.loc[temp['contract'] == instrument, :].reset_index(drop=True)
        # logger.debug(f'getshare3:{time.time()}')
        if len(temp) == 2:
            # һ��һ��
            if temp.loc[0,'direction'] == 'BUY':
                return temp.loc[0,'volume'] - temp.loc[1,'volume']
            elif temp.loc[0,'direction'] == 'SELL':
                return temp.loc[1,'volume'] - temp.loc[0,'volume']
        elif len(temp) == 1:
            # ����
            if temp.loc[0,'direction'] == 'BUY':
                return temp.loc[0, 'volume']
            elif temp.loc[0,'direction'] == 'SELL':
                return - temp.loc[0, 'volume']
        elif len(temp) == 0:
            return 0  # û�гֲ�
        else:
            raise ValueError('��ֲֶ����������飡')

    def getPositions(self, strategyName=None):
        """Returns a dictionary that maps instruments to shares."""
        if strategyName == None:
            strategyName = type(self.strategyNow).__name__  # ��ȡʵ��������
        pos = self.strategyAccount[strategyName].getPosition()
        # logger.debug(f'�ֲ���:\n {pos}')  # test
        return pos

    def getEquity(self, strategyName=None):
        """��ȡ����ֲֵĶ�̬Ȩ��"""
        if strategyName == None:
            strategyName = type(self.strategyNow).__name__  # ��ȡʵ��������
        equity = self.strategyAccount[strategyName].getEquity()
        logger.debug(f'��̬Ȩ���ǣ� {equity}')
        return equity

    def getActiveOrders(self, instrument=None):
        """Returns a dict with the orders that are still active. which the key is order id in tqsdk

        :param instrument: An optional instrument identifier to return only the active orders for the given instrument.
        :type instrument: string.
        """
        res = {}
        allOrder = self.api.get_order()
        for order_id in allOrder:
            if allOrder[order_id].order_id:  # TODO ȷ���Ƿ��������жϣ����������ڸ��µ���, tqsdk ��û���յ��ص���Ϣʱ, �˶����и�������Ϊ��
                # ��id�˾��������ĵ���
                if not allOrder[order_id].is_dead:
                    res[order_id] = allOrder[order_id]
        return res

    def getOrders(self, strategyName=None):
        """���ص������ж���"""
        if strategyName == None:
            strategyName = type(self.strategyNow).__name__  # ��ȡʵ��������
        orders = self.strategyAccount[strategyName].getOrders()
        logger.debug(f'δ���ί���ǣ�{orders}')
        return orders

    def submitOrder(self, order: 'virtualOrder'):
        """Submits an order.
        :param order: The order to submit.
        :type order: :class:`Order`.
        """
        self.strategyAccount[type(self.strategyNow).__name__].addOrder(order)  # �������˻����¶�����
        self.orderQueue.append(order)  # ��ʵ�ʶ��������������Ӷ�������

    def cancelSubmitedOrder(self, order: 'virtualOrder'):
        """
        �����ύ�Ķ������ڻ�û��update��ʱ������Ѿ�update�ˣ�����Ч��Ϊ�˼��ݺ��꽻�׷�����
        """
        for item in self.orderQueue[:]:
            if order.id == item.id:
                self.orderQueue.remove(item)  # ����ҵ���Ҫȡ���µ���id��ȥ�����

    def creatOrder(self, direction, volume, contract, open, strategyName=None, price=None, orderType='Market'):
        """�����µĶ�����"""
        if strategyName == None:
            strategyName = type(self.strategyNow).__name__  # ��ȡʵ��������
        logger.debug('��������->' + str(direction) + str(volume) + str(contract) + str(open) + str(price))

        if orderType == 'Market':  # �м۵�
            return virtualOrder(direction, volume, contract, open, strategyName, oldOrNew='new', tick=self.allTick[contract])
        else:
            return virtualOrder(direction, volume, contract, open, strategyName, oldOrNew='new', price=price,
                                orderType=orderType, tick=self.allTick[contract])

    def createMarketOrder(self, action, instrument, quantity, strategyNmae=None, onClose=False):
        """Creates a Market order.
        A market order is an order to buy or sell a stock at the best available price.
        Generally, this type of order will be executed immediately. However, the price at which a market order will be executed
        is not guaranteed.

        :param strategyNmae: ʹ��������Ե��������µ�
        :param action: The order action.
        :type action: Order.Action.BUY, or Order.Action.BUY_TO_COVER, or Order.Action.SELL or Order.Action.SELL_SHORT.
        :param instrument: Instrument identifier.
        :type instrument: string.
        :param quantity: Order quantity.
        :type quantity: int/float.
        :param onClose: True if the order should be filled as close to the closing price as possible (Market-On-Close order). Default is False.
        :type onClose: boolean.
        :rtype: A :class:`MarketOrder` subclass.
        """
        if action == Action.BUY:
            return self.creatOrder("BUY", quantity, instrument, open=True)
        elif action == Action.SELL_SHORT:
            return self.creatOrder("SELL", quantity, instrument, open=True)
        elif action == Action.SELL:
            return self.creatOrder("SELL", quantity, instrument, open=False)
        elif action == Action.BUY_TO_COVER:
            return self.creatOrder("BUY", quantity, instrument, open=False)
        else:
            raise Exception('unkown action!')

    def createLimitOrder(self, action, instrument, quantity, limitPrice=None):
        """Creates a Limit order.
        A limit order is an order to buy or sell a stock at a specific price or better.
        A buy limit order can only be executed at the limit price or lower, and a sell limit order can only be executed at the
        limit price or higher.

        :param action: The order action.
        :type action: Order.Action.BUY, or Order.Action.BUY_TO_COVER, or Order.Action.SELL or Order.Action.SELL_SHORT.
        :param instrument: Instrument identifier.
        :type instrument: string.
        :param limitPrice: The order price. ���û����дĬ��None�����Ŷ����ż۴���
        :type limitPrice: float
        :param quantity: Order quantity.
        :type quantity: int/float.
        :rtype: A :class:`LimitOrder` subclass.
        """

        if action == Action.BUY:
            if not limitPrice:  # û���ṩ�۸���޼۵���
                limitPrice = self.allTick[instrument].bid_price1
            return self.creatOrder("BUY", quantity, instrument, open=True, price=limitPrice, orderType='Limit')
        elif action == Action.SELL_SHORT:
            if not limitPrice:  # û���ṩ�۸���޼۵���
                limitPrice = self.allTick[instrument].ask_price1
            return self.creatOrder("SELL", quantity, instrument, open=True, price=limitPrice, orderType='Limit')
        elif action == Action.SELL:
            if not limitPrice:  # û���ṩ�۸���޼۵���
                limitPrice = self.allTick[instrument].ask_price1
            return self.creatOrder("SELL", quantity, instrument, open=False, price=limitPrice, orderType='Limit')
        elif action == Action.BUY_TO_COVER:
            if not limitPrice:  # û���ṩ�۸���޼۵���
                limitPrice = self.allTick[instrument].bid_price1
            return self.creatOrder("BUY", quantity, instrument, open=False, price=limitPrice, orderType='Limit')
        else:
            raise Exception('unkown action!')

    def createStopOrder(self, action, instrument, stopPrice, quantity):
        """Creates a Stop order.
        A stop order, also referred to as a stop-loss order, is an order to buy or sell a stock once the price of the stock
        reaches a specified price, known as the stop price.
        When the stop price is reached, a stop order becomes a market order.
        A buy stop order is entered at a stop price above the current market price. Investors generally use a buy stop order
        to limit a loss or to protect a profit on a stock that they have sold short.
        A sell stop order is entered at a stop price below the current market price. Investors generally use a sell stop order
        to limit a loss or to protect a profit on a stock that they own.

        :param action: The order action.
        :type action: Order.Action.BUY, or Order.Action.BUY_TO_COVER, or Order.Action.SELL or Order.Action.SELL_SHORT.
        :param instrument: Instrument identifier.
        :type instrument: string.
        :param stopPrice: The trigger price.
        :type stopPrice: float
        :param quantity: Order quantity.
        :type quantity: int/float.
        :rtype: A :class:`StopOrder` subclass.
        """
        pass

    def createStopLimitOrder(self, action, instrument, stopPrice, limitPrice, quantity):
        """Creates a Stop-Limit order.
        A stop-limit order is an order to buy or sell a stock that combines the features of a stop order and a limit order.
        Once the stop price is reached, a stop-limit order becomes a limit order that will be executed at a specified price
        (or better). The benefit of a stop-limit order is that the investor can control the price at which the order can be executed.

        :param action: The order action.
        :type action: Order.Action.BUY, or Order.Action.BUY_TO_COVER, or Order.Action.SELL or Order.Action.SELL_SHORT.
        :param instrument: Instrument identifier.
        :type instrument: string.
        :param stopPrice: The trigger price.
        :type stopPrice: float
        :param limitPrice: The price for the limit order.
        :type limitPrice: float
        :param quantity: Order quantity.
        :type quantity: int/float.
        :rtype: A :class:`StopLimitOrder` subclass.
        """
        pass

    def create_algo_order(self, action, instrument, quantity, duration=60, min_volume_each_order=1,
                          max_volume_each_order=30, strategyName=None):
        """
        ��������TqSdk���㷨�� ��TWAP��
        :param action: The order action.
        :param instrument: ��Ĵ��룬���ڱ�׼
        :param quantity: ������int
        :param duration: �㷨��������ʱ�䣬int����λ����
        :param min_volume_each_order: (int):������Сί�е���ÿ��ί�е���Ĭ������С�����ֵ�в���
        :param max_volume_each_order:int):�������ί�е���ÿ��ί�е���Ĭ������С�����ֵ�в���
        :return:
        """
        # ֻ�ܴ�����ʽ
        duration = int(20 * (quantity / (max_volume_each_order + min_volume_each_order)))
        # highlight ��Լ�����ڶ���rb 1min��100�ֵ�״̬���Ժ���ܻ���Ҫ���ݲ�ͬ��Ʒ����������Ӧ
        # TODO
        # ���в������ ����tqsdk��������ʽ
        assert quantity >= max_volume_each_order, f'��������С������ʹ���㷨��������{quantity}'
        assert duration * (max_volume_each_order + min_volume_each_order) > 6 * quantity, f'��������Ĭ�ϲ���������'\
                        f'tqsdk�涨��{quantity}, {duration}, {max_volume_each_order}, {min_volume_each_order}'


        logger.debug('�����㷨��')
        if strategyName == None:
            strategyName = type(self.strategyNow).__name__  # ��ȡʵ��������
        pos = self.strategyAccount[strategyName].getPosition()

        if action == Action.BUY:
            return VirtualAlgoOrder("BUY", quantity, instrument, offset='OPEN', duration=duration, min_volume_each_order=min_volume_each_order,
                                    max_volume_each_order=max_volume_each_order, strategyName=strategyName, api=self.api, tick=self.allTick[instrument])
        elif action == Action.SELL_SHORT:
            return VirtualAlgoOrder("SELL", quantity, instrument, offset='OPEN', duration=duration, min_volume_each_order=min_volume_each_order,
                                    max_volume_each_order=max_volume_each_order, strategyName=strategyName, api=self.api, tick=self.allTick[instrument])
        elif action == Action.SELL:
            temp = pos.loc[(pos['direction'] == 'BUY') & (pos['contract']==instrument), ['volume', 'volumeToday']]
            assert temp.shape == (1, 2), 'ͬ����ͬ��ĳ��ֳ���һ���ֲֻ���0���ֲ֣������Ƿ��¼������'  # ���ǲ���ֻ��һ���������ͱ�ĵĳֲ�
            if temp.iloc[0, 0] - temp.iloc[0, 1] >= quantity:  # ����־�ƽ�򣬷���ƽ��
                offset = 'CLOSE'
            else:
                offset = 'CLOSETODAY'
            return VirtualAlgoOrder("SELL", quantity, instrument, offset=offset, duration=duration, min_volume_each_order=min_volume_each_order,
                                    max_volume_each_order=max_volume_each_order, strategyName=strategyName, api=self.api, tick=self.allTick[instrument])
        elif action == Action.BUY_TO_COVER:
            temp = pos.loc[(pos['direction'] == 'SELL') & (pos['contract']==instrument), ['volume', 'volumeToday']]
            assert temp.shape == (1, 2), 'ͬ����ͬ��ĳ��ֳ���һ���ֲֻ���0���ֲ֣������Ƿ��¼������'  # ���ǲ���ֻ��һ���������ͱ�ĵĳֲ�
            if temp.iloc[0, 0] - temp.iloc[0, 1] >= quantity:  # ����־�ƽ�򣬷���ƽ��
                offset = 'CLOSE'
            else:
                offset = 'CLOSETODAY'
            return VirtualAlgoOrder("BUY", quantity, instrument, offset=offset, duration=duration, min_volume_each_order=min_volume_each_order,
                                    max_volume_each_order=max_volume_each_order, strategyName=strategyName, api=self.api, tick=self.allTick[instrument])
        else:
            raise Exception('unkown action!')

    def cancelOrder(self, order: 'tqsdk.order'):
        """Requests an order to be canceled. If the order is filled an Exception is raised.
        :param order: The order to cancel.
        :type order: :class:`Order`.
        �Եײ�ĳ������������ֱ����Ҫ������Ҫ��virtualAccountHelpʵ�֡�
        """
        if not order.is_dead:
            self.api.cancel_order(order.order_id)
        logger.debug('�����Ķ���id�ǣ� ' + str(order.id))

    def getMainContract(self, symbol):
        return self.allMainTick['KQ.m@' + symbol].underlying_symbol

    def setAllMainContract(self, tick):
        self.allMainContract = tick
    # ===============================================================
    def start(self):  # pyalgotrade ����abstract methods ��д���ɡ�
        pass

    def stop(self):  # pyalgotrade ����abstract methods ��д���ɡ�
        temp = pd.concat([self.strategyAccount[key].account for key in self.strategyAccount.keys()])
        temp.reset_index(drop=True, inplace=True)
        if datetime.now().hour == 15:  # �������̲Űѽ�ָ�Ϊ���
            for i in range(temp.shape[0]):
                if temp.loc[i, 'account'] != 1:
                    if temp.loc[i, 'volumeToday'] != 0:
                        temp.loc[i, 'volumeToday'] = 0  # ȥ�����еĽ�ּ�¼

        # highlight ��δ��ɵĶ�����¼�� Record.json,�����´�������ʱ���ٴ��µ���
        with open(self.context.Record_path) as f:
            record = json.load(f)

        temp1 = []
        for order in self.unfilledQueue:
            temp1.append(order.getCreatParameter())
        for order in self.reInsertQueue:
            temp1.append(order.getCreatParameter())

        if temp1:  # �������[]�ͼ�¼
            with open(self.context.Record_path, 'w') as f:
                record["unfilledOrder"] = temp1
                json.dump(record, f)
                logger.warning('������δ��ɵĶ��������鲢�ڿ������£�')

        temp.to_csv(self.context.current_account_path, index=False)

    def write(self):
        temp = pd.concat([self.strategyAccount[key].account for key in self.strategyAccount.keys()])
        temp.to_csv(self.context.current_account_path, index=False)
        logger.debug('write position to file \n {temp}')

    def join(self):  # pyalgotrade ����abstract methods ��д���ɡ�
        pass

    def eof(self):  # pyalgotrade ����abstract methods ��д���ɡ�
        pass

    def dispatch(self):  # pyalgotrade ����abstract methods ��д���ɡ�
        pass

    def peekDateTime(self):  # pyalgotrade ����abstract methods ��д���ɡ�
        pass

    def inTradeTime(self, tick: Union[str, 'tqsdk.tick']) -> bool:
        """����ĳ��tick���Լ���룬�ж��Ƿ���ʱ����"""
        # highlight ���������ҹ��Ҳ�ֶν�����δ�����Ҫ�ġ�
        if type(tick) == str:
            tick = self.allTick[tick]

        now = datetime.now().time()
        if now.hour > 15 or now.hour < 8:  # ҹ��
            if not tick.trading_time.night:  # û��ҹ�̵ı�Ļ��ǿ�list��
                return False

            if tick.trading_time.night[0][0] < str(now) < tick.trading_time.night[0][1]:  # ҹ���ڽ���ʱ���ڡ�
                return True
        else:  # ����
            for _time in tick.trading_time.day:
                if _time[0] < str(now) < _time[1]:
                    return True
            else:
                return False
        return False

    def current(self, contract):
        """ ��ȡ���µ�һ��tick"""
        pass

    def update(self):
        """ÿһ��Onbarѭ��������������ʱ������е���Ҫ���µ����񣬱�����������˻�����������������ȡ�"""
        # print(self.allTick['SHFE.rb2010']['last_price'])
        self.cash = self.accountInfo.available
        self.balence = self.accountInfo.balance
        logger.debug(f'real account cash is: {self.cash}')
        logger.debug(f'real account balance is: {self.balence}')
        """--------------------------------------------�µ�----------------------------------------------"""
        #  ע�����е�Queue����ȫ������VirtualOrder
        if self.orderQueue:  # ����������ж�����
            logger.debug('��Ҫ�µĵ��ֱ��ǣ�')
            groupbyOrder = {}  # ��һ��dict��װ���ඩ����
            for order in self.orderQueue:
                if not self.inTradeTime(self.allTick[order.virContract]):  # �Ƿ���ʱ�����������
                    continue
                logger.debug(str(order))
                if not order.volumeLeft:
                    logger.debug('����Ϊ0��ֱ��������~')
                    order.is_dead = True
                    continue

                # ������㷨��ֱ�������������д���
                if order.orderType == 'Algo':
                    logger.info(f'�µ�֪ͨ��\n {str(order)}')
                    continue

                if not order.virContract in groupbyOrder:  # ��û�������Լ
                    groupbyOrder[order.virContract] = {'long': [], 'short': [], 'other': []}
                    # ��ʼ��Ϊdict Of list , ��Ϊ�������, �޼۵�ֱ�ӽ���other ����Գ�
                if order.orderType == 'Limit':  # �޼۵�
                    groupbyOrder[order.virContract]['other'].append(order)
                else:
                    if order.virDirection == 'BUY':  # ���������߷��ࡣ
                        groupbyOrder[order.virContract]['long'].append(order)
                    else:
                        groupbyOrder[order.virContract]['short'].append(order)

            logger.debug('������ɣ�dict�ǣ� ')
            logger.debug(str(groupbyOrder))

            for contract in groupbyOrder:  # �Է���õ����ⵥ����ѭ��������û�п��Ի�������ġ�
                if groupbyOrder[contract]['long'] and groupbyOrder[contract]['short']:  # ȷ�����������ǿ�
                    b = 0  # ��ʼ������ָ�룬 ����ȷ����ȥ��������list���Ǹ�λ�á�
                    s = 0
                    while True:
                        if groupbyOrder[contract]['long'][b].volumeLeft == groupbyOrder[contract]['short'][
                            s].volumeLeft:
                            logger.debug('1')
                            groupbyOrder[contract]['long'][b].is_dead = True  # ���ֱ��������������ȫ��dead��λ�ü�һ
                            groupbyOrder[contract]['short'][s].is_dead = True
                            b += 1
                            s += 1

                        elif groupbyOrder[contract]['long'][b].volumeLeft < groupbyOrder[contract]['short'][
                            s].volumeLeft:
                            logger.debug('2')
                            groupbyOrder[contract]['long'][b].is_dead = True
                            groupbyOrder[contract]['short'][s].volumeLeft = \
                                groupbyOrder[contract]['short'][s].volumeLeft - groupbyOrder[contract]['long'][
                                    b].volumeLeft
                            b += 1

                        elif groupbyOrder[contract]['long'][b].volumeLeft > groupbyOrder[contract]['short'][
                            s].volumeLeft:
                            logger.debug('3')
                            groupbyOrder[contract]['short'][s].is_dead = True
                            groupbyOrder[contract]['long'][b].volumeLeft = \
                                groupbyOrder[contract]['long'][b].volumeLeft - groupbyOrder[contract]['short'][
                                    s].volumeLeft
                            s += 1

                        else:
                            raise Exception('������۳�������')

                        if b == len(contract['long']) or s == len(contract['short']):
                            break
            logger.debug('������ɣ�����ǣ�')
            logger.debug(str(groupbyOrder))

            for item in groupbyOrder:  # �����µ�
                pos = self.posDict.get(item, None)
                if pos:  # ���Լ��Ľ���broker������ͬһ����Լ��ͬʱ�ж�ղֲֳֵĳ��֡����ǲ������ġ�
                    availablePos = pos.pos_long - pos.pos_short
                    availablePosToday = pos.pos_long_today - pos.pos_short_today
                else:
                    availablePos = 0
                    availablePosToday = 0
                logger.debug(f'availablePos:{availablePos},:: availablePosToday:{availablePosToday}')

                if groupbyOrder[item]['long']:
                    for order in groupbyOrder[item]['long']:
                        if availablePos >= 0:  # Ҫ�򣬳ֲִ���0 ��ô�������ⵥ��ƽ����Ҫ����
                            logger.debug('�µ�1')
                            logger.info('�µ�֪ͨ��\n' + str(order))
                            res = self.api.insert_order(order.contract, order.direction,
                                                        'OPEN', order.volumeLeft,
                                                        self.allTick[order.virContract]['upper_limit'])
                            order.attach(res)
                            self.unfilledQueue.append(order)
                            # availablePos -= order.volumeLeft
                        elif availablePos < 0:
                            if abs(availablePos) >= order.volumeLeft:  # ��Ҫ���׵����������У�����һ��ֱ��ƽ
                                logger.debug('�µ�2')
                                logger.info('�µ�֪ͨ��\n' + str(order))
                                if abs(availablePosToday) >= order.volumeLeft:
                                    res = self.api.insert_order(order.contract, order.direction,
                                                                'CLOSETODAY', order.volumeLeft,
                                                                self.allTick[order.virContract]['upper_limit'])
                                    availablePosToday += order.volumeLeft

                                elif abs(availablePosToday) < order.volumeLeft:  # ����Ĭ����ƽ����ƽ��
                                    if availablePosToday:  # ����н�־���ƽ��
                                        res1 = self.api.insert_order(order.contract, order.direction,
                                                                     'CLOSETODAY', abs(availablePosToday),
                                                                     self.allTick[order.virContract]['upper_limit'])
                                        order.attach(res1)

                                    res = self.api.insert_order(order.contract, order.direction,
                                                                'CLOSE', order.volumeLeft - abs(availablePosToday),
                                                                self.allTick[order.virContract]['upper_limit'])
                                    availablePosToday = 0

                                order.attach(res)
                                self.unfilledQueue.append(order)
                                availablePos += order.volumeLeft
                            else:  # ��ƽ�ٿ�
                                logger.debug('�µ�3')
                                logger.info('�µ�֪ͨ��\n' + str(order))

                                if availablePosToday:  # ����н�֣�����ƽ���
                                    res = self.api.insert_order(order.contract, order.direction,
                                                                'CLOSETODAY', abs(availablePosToday),
                                                                self.allTick[order.virContract]['upper_limit'])
                                    order.attach(res)
                                if abs(availablePos) > abs(availablePosToday):
                                    # ƽ��������ƽ���
                                    res1 = self.api.insert_order(order.contract, order.direction,
                                                                 'CLOSE', abs(availablePos) - abs(availablePosToday),
                                                                 self.allTick[order.virContract]['upper_limit'])
                                    order.attach(res1)
                                if order.volumeLeft > abs(availablePos):  # �����������Ҫ����
                                    # ���������
                                    res2 = self.api.insert_order(order.contract, order.direction,
                                                                 'OPEN', order.volumeLeft - abs(availablePos),
                                                                 self.allTick[order.virContract]['upper_limit'])

                                    order.attach(res2)
                                self.unfilledQueue.append(order)
                                availablePosToday = 0
                                availablePos = 0

                elif groupbyOrder[item]['short']:
                    for order in groupbyOrder[item]['short']:
                        if availablePos <= 0:  # Ҫ�����ֲ�С��0
                            logger.debug('�µ�4')
                            logger.info('�µ�֪ͨ��\n' + str(order))
                            res = self.api.insert_order(order.contract, order.direction,
                                                        'OPEN', order.volumeLeft,
                                                        self.allTick[order.virContract]['lower_limit'])
                            order.attach(res)
                            self.unfilledQueue.append(order)
                            # availablePos += order.volumeLeft
                        elif availablePos > 0:
                            if availablePos >= order.volumeLeft:  # ��Ҫ���׵����������У�����һ��ֱ��ƽ
                                logger.debug('�µ�5')
                                logger.info('�µ�֪ͨ��\n' + str(order))

                                if availablePosToday >= order.volumeLeft:  # ��ִ���Ҫ���ף�ֱ��ƽ
                                    res = self.api.insert_order(order.contract, order.direction,
                                                                'CLOSETODAY', order.volumeLeft,
                                                                self.allTick[order.virContract]['lower_limit'])
                                    availablePosToday -= order.volumeLeft

                                elif availablePosToday < order.volumeLeft:  # ����Ĭ����ƽ����ƽ��
                                    if availablePosToday:  # ����н�־���ƽ��
                                        res1 = self.api.insert_order(order.contract, order.direction,
                                                                     'CLOSETODAY', abs(availablePosToday),
                                                                     self.allTick[order.virContract]['lower_limit'])
                                        order.attach(res1)

                                    res = self.api.insert_order(order.contract, order.direction,
                                                                'CLOSE', order.volumeLeft - abs(availablePosToday),
                                                                self.allTick[order.virContract]['lower_limit'])
                                    availablePosToday = 0

                                order.attach(res)
                                self.unfilledQueue.append(order)
                                availablePos -= order.volumeLeft

                            else:
                                logger.debug('�µ�6')
                                logger.info('�µ�֪ͨ��\n' + str(order))
                                if availablePosToday:  # ����н�֣�����ƽ���
                                    res = self.api.insert_order(order.contract, order.direction,
                                                                'CLOSETODAY', abs(availablePosToday),
                                                                self.allTick[order.virContract]['lower_limit'])
                                    order.attach(res)
                                if abs(availablePos) > abs(availablePosToday):
                                    # ƽ��������ƽ���
                                    res1 = self.api.insert_order(order.contract, order.direction,
                                                                 'CLOSE', abs(availablePos) - abs(availablePosToday),
                                                                 self.allTick[order.virContract]['lower_limit'])
                                    order.attach(res1)
                                if order.volumeLeft > abs(availablePos):  # �����������Ҫ����
                                    # ���������
                                    res2 = self.api.insert_order(order.contract, order.direction,
                                                                 'OPEN', order.volumeLeft - abs(availablePos),
                                                                 self.allTick[order.virContract]['lower_limit'])

                                    order.attach(res2)
                                self.unfilledQueue.append(order)
                                availablePosToday = 0
                                availablePos = 0

                if groupbyOrder[item]['other']:
                    for order in groupbyOrder[item]['other']:
                        if order.virDirection == 'SELL':
                            if availablePos <= 0:  # Ҫ�����ֲ�С��0
                                logger.debug('�µ�7')
                                logger.info('�µ�֪ͨ��\n' + str(order))
                                res = self.api.insert_order(order.contract, order.direction,
                                                            'OPEN', order.volumeLeft,
                                                            order.price)
                                order.attach(res)
                                self.unfilledQueue.append(order)
                                # availablePos += order.volumeLeft
                            elif availablePos > 0:
                                if availablePos > order.volumeLeft:  # ��Ҫ���׵����������У�����һ��ֱ��ƽ
                                    logger.debug('�µ�8')
                                    logger.info('�µ�֪ͨ��\n' + str(order))

                                    if availablePosToday >= order.volumeLeft:  # ��ִ���Ҫ���ף�ֱ��ƽ
                                        res = self.api.insert_order(order.contract, order.direction,
                                                                    'CLOSETODAY', order.volumeLeft,
                                                                    order.price)
                                        availablePosToday -= order.volumeLeft

                                    elif availablePosToday < order.volumeLeft:  # ����Ĭ����ƽ����ƽ��
                                        if availablePosToday:  # ����н�־���ƽ��
                                            res1 = self.api.insert_order(order.contract, order.direction,
                                                                         'CLOSETODAY', abs(availablePosToday),
                                                                         order.price)
                                            order.attach(res1)

                                        res = self.api.insert_order(order.contract, order.direction,
                                                                    'CLOSE', order.volumeLeft - abs(availablePosToday),
                                                                    order.price)
                                        availablePosToday = 0

                                    order.attach(res)
                                    self.unfilledQueue.append(order)
                                    availablePos -= order.volumeLeft
                                else:
                                    logger.debug('�µ�9')
                                    logger.info('�µ�֪ͨ��\n' + str(order))
                                    if availablePosToday:  # ����н�֣�����ƽ���
                                        res = self.api.insert_order(order.contract, order.direction,
                                                                    'CLOSETODAY', abs(availablePosToday),
                                                                    order.price)
                                        order.attach(res)
                                    if abs(availablePos) > abs(availablePosToday):
                                        # ƽ��������ƽ���
                                        res1 = self.api.insert_order(order.contract, order.direction,
                                                                     'CLOSE',
                                                                     abs(availablePos) - abs(availablePosToday),
                                                                     order.price)
                                        order.attach(res1)
                                    if order.volumeLeft > abs(availablePos):  # �����������Ҫ����
                                        # ���������
                                        res2 = self.api.insert_order(order.contract, order.direction,
                                                                     'OPEN', order.volumeLeft - abs(availablePos),
                                                                     order.price)

                                        order.attach(res2)
                                    self.unfilledQueue.append(order)
                                    availablePosToday = 0
                                    availablePos = 0

                        elif order.virDirection == 'BUY':
                            if availablePos >= 0:  # Ҫ�����ֲ�С��0
                                logger.debug('�µ�10')
                                logger.info('�µ�֪ͨ��\n' + str(order))
                                res = self.api.insert_order(order.contract, order.direction,
                                                            'OPEN', order.volumeLeft,
                                                            order.price)
                                order.attach(res)
                                self.unfilledQueue.append(order)
                                # availablePos += order.volumeLeft
                            elif availablePos < 0:
                                if abs(availablePos) > order.volumeLeft:  # ��Ҫ���׵����������У�����һ��ֱ��ƽ
                                    logger.debug('�µ�11')
                                    logger.info('�µ�֪ͨ��\n' + str(order))
                                    if abs(availablePosToday) >= order.volumeLeft:  # ��ִ���Ҫ���ף�ֱ��ƽ
                                        res = self.api.insert_order(order.contract, order.direction,
                                                                    'CLOSETODAY', order.volumeLeft,
                                                                    order.price)
                                        availablePosToday -= order.volumeLeft

                                    elif abs(availablePosToday) < order.volumeLeft:  # ����Ĭ����ƽ����ƽ��
                                        if availablePosToday:  # ����н�־���ƽ��
                                            res1 = self.api.insert_order(order.contract, order.direction,
                                                                         'CLOSETODAY', abs(availablePosToday),
                                                                         order.price)
                                            order.attach(res1)

                                        res = self.api.insert_order(order.contract, order.direction,
                                                                    'CLOSE', order.volumeLeft - abs(availablePosToday),
                                                                    order.price)
                                        availablePosToday = 0

                                    order.attach(res)
                                    self.unfilledQueue.append(order)
                                    availablePos += order.volumeLeft
                                else:
                                    logger.debug('�µ�12')
                                    logger.info('�µ�֪ͨ��\n' + str(order))
                                    if availablePosToday:  # ����н�֣�����ƽ���
                                        res = self.api.insert_order(order.contract, order.direction,
                                                                    'CLOSETODAY', abs(availablePosToday),
                                                                    order.price)
                                        order.attach(res)
                                    if abs(availablePos) > abs(availablePosToday):
                                        # ƽ��������ƽ���
                                        res1 = self.api.insert_order(order.contract, order.direction,
                                                                     'CLOSE',
                                                                     abs(availablePos) - abs(availablePosToday),
                                                                     order.price)
                                        order.attach(res1)
                                    if order.volumeLeft > abs(availablePos):  # �����������Ҫ����
                                        # ���������
                                        res2 = self.api.insert_order(order.contract, order.direction,
                                                                     'OPEN', order.volumeLeft - abs(availablePos),
                                                                     order.price)

                                        order.attach(res2)
                                    self.unfilledQueue.append(order)
                                    availablePosToday = 0
                                    availablePos = 0

            self.orderQueue = []  # ���¹��㡣

        """-------------------------------------δ�ɽ�����������ʱ���յȴ�5�볷��������-----------------------------"""
        if self.unfilledQueue:
            logger.debug('����δ�ɽ��ĵ�')
            logger.debug('unfilled queue ������ǣ�')
            for i in self.unfilledQueue:
                logger.debug(str(i))
            tempList = []
            while self.unfilledQueue:
                temp = self.unfilledQueue.pop()  # �ѵ�һ���ó��������û��dead������append��ȥ��append��ĩβ����������ѭ��һ�Ρ�
                logger.debug(f'�жϵĶ����ǣ�{temp}')
                if not temp.is_dead:  # ���dead�˾Ͳ���append�����ˣ�ֱ�Ӳ����ˡ�
                    # if not temp.insert_date_time:
                    #     temp.insert_date_time = time.time()
                    logger.debug('�е�δ��,�����ǣ�')
                    logger.debug(str(temp.id))
                    logger.debug('�ҵ�ʱ���ǣ�')
                    logger.debug(temp.insert_date_time)
                    if time.time() - temp.insert_date_time > 5:
                        # ����5�룬�Ҽ۸������ų���
                        logger.debug('��ʱδ�ɽ���')
                        if temp.direction == 'BUY':
                            if temp.limit_price != self.allTick[temp.instrument_id].bid_price1:
                                self.cancelOrderQueue.append(temp)
                                logger.debug('Ҫ��' + str(temp))
                                continue
                                # ֻҪ������queue�����˾Ͳ��ڱ�����δ�ɽ�queue���������ڳ���״̬�����ӳٶ����´˴��־��������
                                # ��Ҫ���������������ӵ�����queue�У�����ͬһ���������ƺܶ�Σ������continueһ����
                        else:
                            if temp.limit_price != self.allTick[temp.instrument_id].ask_price1:
                                self.cancelOrderQueue.append(temp)
                                logger.debug('Ҫ��' + str(temp))
                                continue
                    tempList.append(temp)
                else:  # �˵��Ѿ������ˣ�������ȫ�ɽ��ˡ�
                    logger.debug('�˵�����ˣ��������ˣ�������ȫ�ɽ��ˣ����������')
                    logger.info('�ɽ�֪ͨ��\n' + str(temp))

            self.unfilledQueue = tempList


        """--------------------------------------------����--------------------------------------------------------"""
        if self.cancelOrderQueue:  # ����������
            logger.debug('��������')
            while self.cancelOrderQueue:
                temp = self.cancelOrderQueue.pop()
                logger.debug('��' + str(temp))
                for i in temp.realOrder:
                    if not i.is_dead:
                        self.api.cancel_order(i.order_id)
                self.reInsertQueue.append(temp)

        """------------------------�ȴ���������鵽�������ùҵ������������µ�-----------------------------------------"""
        # tqsdk��Ҫ������wait_update�Ժ���ܼ�⵽������
        if self.reInsertQueue:
            logger.debug('����Ƿ��������')
            tempList = []
            while self.reInsertQueue:
                temp = self.reInsertQueue.pop()
                if temp.is_dead:  # tqsdkȷ���ˣ��������¡�
                    if temp.orderType == 'Limit':
                        # �������޼۵�
                        logger.debug('�����޼۵�')
                        if temp.virDirection == 'BUY':
                            price = self.allTick[temp.virContract].bid_price1
                        elif temp.virDirection == 'SELL':
                            price = self.allTick[temp.virContract].ask_price1
                        else:
                            logger.error('�����µ�����δ֪���µ��������飡')

                        self.submitOrder(virtualOrder(temp.virDirection, temp.volumeLeft, temp.virContract,
                                                      temp.open, type(self.strategyNow).__name__, price=price,
                                                      oldOrNew='new', orderType='Limit', tick=self.allTick[temp.virContract]))
                    elif temp.orderType == 'Market':
                        # �����µ�
                        logger.debug('�����м۵�')
                        if temp.virDirection == 'BUY':
                            price = self.allTick[temp.virContract].upper_limit
                        elif temp.virDirection == 'SELL':
                            price = self.allTick[temp.virContract].lower_limit
                        else:
                            logger.error('�����µ�����δ֪���µ��������飡')

                        self.orderQueue.append(virtualOrder(temp.virDirection, temp.volumeLeft,
                                                            temp.virContract, temp.open,
                                                            type(self.strategyNow).__name__,
                                                            price=price, oldOrNew='new', orderType='Market',
                                                            tick=self.allTick[temp.virContract]))
                    else:
                        logger.error('�����µ������������⣬���飡')
                else:
                    tempList.append(temp)
            self.reInsertQueue = tempList
            logger.debug('��Ҫ����Ƿ����µ�queue�ǣ�')
            for i in self.reInsertQueue:
                logger.debug(str(i))

        """------------------------------------------���¸��������˻���------------------------------------------"""
        temp = []
        all_filled_order = {}
        for item in self.strategys:  # ���¸��������˻�����
            write, filled_order = self.strategyAccount[item].update(self.allTick, self.posDict)
            temp.append(write)
            all_filled_order[item] = filled_order

        if any(temp):
            self.write()  # �ж������¾�д�ļ���
        logger.debug(f'update���')
        return all_filled_order
        # brokerû��stg�����޷��ص����ԣ�����ʱ�������Իص������ϲ� type: dict of list


class _virtualAccountHelp:
    """
    �������㲻ͬ���Ե�����ֲ�
    """
    def __init__(self, account):
        """
        :param account��ʾ�ӱ����ļ���ȡ��ʱ���ж��ٸ��ֲֳֵ�df
        """
        self.orders = []
        self.account = account.reset_index(drop=True)
        try:
            self.balance = self.account['balance'][0]
        except:
            # ��������ڻ���һ���յ�DF��
            raise Exception('����Ƿ���currentAccount�������������в��Գֲ֣�')

    def getCash(self):
        temp = self.balance - self.account['funds_occupied'].sum()
        logger.debug(f'���Ի�ȡcash�ǣ�{temp}')
        return temp

    def getPosition(self):
        logger.debug(f'���Ի�ȡ�ֲ��ǣ�{self.account}')
        return self.account

    def getEquity(self):
        temp = self.account['balance'][0]
        logger.debug(f'���Ի�ȡȨ���ǣ�{temp}')
        return temp

    def getOrders(self):
        logger.debug('��ȡorder�ǣ�')
        for order in self.orders:
            logger.debug(f'{order}')
        return self.orders

    def addOrder(self, order: 'virtualOrder'):
        """
        ��¼���µ��µ������order
        :param order:
        :return:
        """
        logger.debug(f'��������attach�����ǣ�{order}')
        self.orders.append(order)

    def update(self, allTick: dict, position: dict):
        """
        �������µ�������߳ɽ��������˻�
        :param allTick: ��������Ʒ�ֵ�Tick��dict
        :param position: tqsdk��position�� ��������ÿ����ͬ��Լ�ı�֤��ռ�á�
        :return:
        """
        # test ����log̫������ʱ�Ȳ���¼��
        # logger.debug(f'�����pos�ǣ�{position}, tick�ǣ�{allTick}')
        write = False
        filled_order = []  # ������¼��Щ��������˷��غ󣬷���ص����ԡ�
        # ���������ĸ��¡�����position account ���� position
        for order in self.orders[:]:  # ��ԭlist���п�����������Ϊɾ������index���
            logger.debug('���������仯')
            logger.debug(f'�����ǣ�{order}')
            if order.is_dead:
                logger.debug('����ɵĶ���')
                # ���ڶ������ظ��־ܾ���Ϣֱ�ӱ���
                assert not order.is_error, f'�����д��󣬴�����Ϣ��{order.last_msg}'
                self.orders.remove(order)
                filled_order.append(order)
                if order.virVolume != order.volumeLeft:
                    assert order.virVolume > order.volumeLeft, f'ʣ����{order.volumeLeft}���ڳ�ʼ��{order.virVolume}����!'
                    # ���ҵ����Ƿ��ʣ������ȣ�������ȫδ�ɽ��ĳ������������³ֲֵ��³������⡣ ����ᱻ����һ���ֲ�����Ϊ0�ĳ�
                    # �ּ�¼
                    if not order.filledPrice:
                        time.sleep(0.5)
                        # ��ʱ����ܵ�ʱis_deadΪTrue�ˣ����ǳɽ����۵ĳɽ���TqSdk��ȡ�Ļ���0�����Է���nan��Ȼ��filledPrice����None��Ҫ�������ж�
                        assert order.filledPrice, '�ȴ�0.5���鵽�ĳɽ��ۻ���None�����飡'

                    tempAcount = self.account.groupby(by=['contract']).apply(lambda x: x)
                    tempStr = str(order.contract)
                    logger.debug('�жϸ���ô����')
                    if not tempAcount.empty and tempAcount['contract'].str.contains(tempStr).any():  # ˵��������ֲ�
                        if order.open:  # ����
                            for i in range(len(self.account)):
                                if self.account.loc[i, 'direction'] == order.direction and \
                                    self.account.loc[i, 'contract'] == order.contract:  # �ж��Ƿ���ͬ��ֲ�
                                    logger.debug(f'ѭ��������i�ǣ�{i}, ���Գֲ�״���ǣ�{self.account}')
                                    # ���³ֲ־���
                                    self.account.loc[i, 'avgOpenPrice'] = (self.account.loc[i, 'avgOpenPrice'] *
                                                                           self.account.loc[i, 'volume'] + \
                                                                           order.filledPrice * order.filledVolume) / (
                                                                                  order.filledVolume + self.account.loc[
                                                                              i, 'volume'])
                                    # ���½�ֺ��ܲ�
                                    self.account.loc[i, 'volumeToday'] += order.filledVolume
                                    self.account.loc[i, 'volume'] += order.filledVolume
                                    break
                            else:
                                logger.debug('�����³ֲ�')
                                logger.debug(f'���Գֲ�״���ǣ�{self.account}')
                                self.account.loc[len(self.account), ['strategy', 'direction', 'volume', 'volumeToday',
                                                                     'contract', 'prePrice', 'avgOpenPrice']] = \
                                    [order.strategyName, order.virDirection, order.filledVolume, order.filledVolume,
                                     order.virContract,
                                     order.filledPrice, order.filledPrice]
                        else:  # ƽ��
                            logger.debug('�޸ľɳֲ֡�')
                            # �ж���Ҫ��Ѱ��direction
                            if order.direction == 'BUY':
                                serchDirc = 'SELL'
                            elif order.direction == 'SELL':
                                serchDirc = 'BUY'
                            else:
                                raise Exception('���������ֶ�����')

                            for i in range(len(self.account)):
                                logger.debug(f'���Գֲ�״���ǣ�{self.account}')
                                if self.account.loc[i, 'contract'] == tempStr:
                                    if self.account.loc[i, 'direction'] == serchDirc:  # �ж��Ƿ��з���ֲ�
                                        if self.account.loc[i, 'volume'] >= order.filledVolume:
                                            if self.account.loc[i, 'volume'] - self.account.loc[
                                                i, 'volumeToday'] < order.filledVolume:
                                                # ���С��ƽ���ģ����ҲҪ���٣��˴�Ĭ���ȿ���ƽ
                                                self.account.loc[i, 'volumeToday'] -= \
                                                    (order.filledVolume - (self.account.loc[i, 'volume'] - self.account.loc[
                                                        i, 'volumeToday']))

                                            self.account.loc[i, 'volume'] -= order.filledVolume
                                            # ƽ���п��ܻᵼ������Ϊ0����Ҫ����Ƿ�ȥ����һ���ֲ�
                                            if self.account.loc[i, 'volume'] == 0:
                                                if self.account.loc[i, 'volumeToday'] == 0:
                                                    self.account.drop(i, inplace=True)
                                                    self.account.reset_index(drop=True, inplace=True)
                                                else:
                                                    raise Exception('volumeΪ0��wolumeToday��Ϊ0��')
                                        else:
                                            raise Exception('ƽ�ֳ����ֲ�������')
                                        break
                            else:  # û�����췽��ĳֱֲ���
                                raise Exception('û����Ҫƽ�ķ����෴�ĳֲ�')
                    else:  # û�����Ʒ�ֵĳֲ�
                        if order.open:  # ����
                            logger.debug('�����³ֲ�')
                            logger.debug(f'���Գֲ�״���ǣ�{self.account}')
                            self.account.loc[len(self.account), ['strategy', 'direction', 'volume', 'volumeToday',
                                                                 'contract', 'prePrice', 'avgOpenPrice']] = \
                                [order.strategyName, order.virDirection, order.filledVolume, order.filledVolume,
                                 order.virContract,
                                 order.filledPrice, order.filledPrice]
                        else:
                            raise Exception('û�п�ƽ�ֲ֣�')

                self.account['balance'] -= order.fee  # ȥ����ν��׵������ѡ�
                self.account['fee'] += order.fee
                write = True  # �������״̬�����˱仯�͸�д�ļ�
        logger.debug('�����궩���Ժ���˻�����ǣ�')
        logger.debug(str(self.account))

        # logger.debug('tick �ǣ�')
        # logger.debug(str(allTick))
        for i in range(len(self.account)):
            # �ж��Ƿ��TqSdk��ȡ��������Nan���оͱ�����
            if self.account.loc[i, 'account'] == 1 or  math.isnan(allTick[self.account.loc[i, 'contract']].last_price) or \
            math.isnan(allTick[self.account.loc[i, 'contract']].volume_multiple):
                continue  # ���Ͽ���ǰ��������¼�Ϊ�յ���������Բ��޸ģ�ֱ������
                # logger.error(f"""Ȩ��������Nan����ʱ�����˻��������ǣ���Լ��{self.account.loc[i, 'contract']};
                            # ���¼�{allTick[self.account.loc[i, 'contract']].last_price };
                            # ��Լ����{allTick[self.account.loc[i, 'contract']].volume_multiple}""")

            if self.account.loc[i, 'direction'] == 'BUY':
                # ����Ȩ�棬��tick�ı䶯���ϳ��е��������ٳ��Ϻ�Լ������
                self.account['balance'] += \
                    (allTick[self.account.loc[i, 'contract']].last_price - self.account.loc[i, 'prePrice']) \
                    * self.account.loc[i, 'volume'] * allTick[self.account.loc[i, 'contract']].volume_multiple

            elif self.account.loc[i, 'direction'] == 'SELL':
                # ����Ȩ�棬��tick�ı䶯���ϳ��е��������ٳ��Ϻ�Լ������
                self.account['balance'] -= \
                    (allTick[self.account.loc[i, 'contract']].last_price - self.account.loc[i, 'prePrice']) \
                    * self.account.loc[i, 'volume'] * allTick[self.account.loc[i, 'contract']].volume_multiple
            if self.account.loc[i, 'account'] != 1:  # ���Ǽ�¼��Ϣ���У��ż���
                self.account.loc[i, 'prePrice'] = allTick[self.account.loc[i, 'contract']].last_price
                pos = position[self.account.loc[i, 'contract']]
                # ��ʱ�����ֲֳָ��µĲ���order�������������ͻ�����б�֤��ռ�ã������ֲܳ�Ϊ0�����
                if pos.pos_long + abs(pos.pos_short) != 0:
                    self.account.loc[i, 'funds occupied'] = \
                        pos.margin / (abs(pos.pos_long) + abs(pos.pos_short)) * self.account.loc[i, 'volume']

        logger.debug('�˻�����ǣ�')
        logger.debug(str(self.account))
        # print(time.ctime())
        # print(self.account)

        logger.debug(f'���Ը������')
        return write, filled_order




class virtualOrder:
    """
    ģ��Ķ����࣬ ��Ϊ�����˻���ʱ���Ǽ������ⵥ����ͬһ��ʵ�̵�������Ҫ��д��
    """
    count = [0]  # ������¼���촴���˶��ٶ�������ȷ��ÿ��ʵ����������ظ���id

    def __init__(self, direction, volume, contract, open, strategyName, tick, oldOrNew='old', price=None, orderType='Market'):
        """

        :param tick �µ�Ʒ�ֵ�ʵʱtick ������¼ͳ�Ƴɽ�����
        :param direction:
        :param volume:
        :param contract:
        :param open: �Ƿ��ǿ���
        :param oldOrNew:  Ĭ����֡�
        """
        self.virDirection = direction
        self.virVolume = volume
        self.virContract = contract
        self.open = open
        self.oldOrNew = oldOrNew
        self.time = time.time()  # ��¼����ʱ��
        self.id = self.count[0]
        self.__volumeLeft = volume  # �ѳɽ�����
        self.__is_dead = False
        self.count[0] += 1
        self.strategyName = strategyName
        self.orderType = orderType

        self.realOrder = []
        self.price = price
        self.tick = tick
        logger.debug(self.__str__())

    def __str__(self):
        # return 'direction: {},\n volume: {},\n volumeLeft: {},\n contract: {},\n open: {},\n orderType: {},\n oldOrNew��{},\n time: {},\n ' \
        #        '.isdead: {},\n strategyName: {},\n countNum: {},\n price: {}, \norderedPrice{}'.format(self.virDirection, self.virVolume,
        #                                                                              self.volumeLeft,
        #                                                                              self.virContract, self.open,
        #                                                                              self.orderType, self.oldOrNew,
        #                                                                              self.time,
        #                                                                              self.is_dead, self.strategyName,
        #                                                                              self.id, self.filledPrice, self.price)
        
        return f"""order info :::direction: {self.virDirection},\n volume: {self.virVolume},\n volumeLeft: {self.volumeLeft},
                contract: {self.virContract},\n open: {self.open},\n orderType: {self.orderType},
                oldOrNew��{self.oldOrNew},\n time: {self.time},
                isdead: {self.is_dead},\n strategyName: {self.strategyName},\n countNum: {self.id},
                price: {self.filledPrice}, \norderedPrice{self.price}, \ntick: {self.tick}"""

    def __repr__(self):
        """���㽫������洢��json�ļ�"""
        return f"""{{"direction": {self.virDirection}, "volume": {self.virVolume}, "contract": {self.virContract}, "open": {self.open}, \
        "strategyName": {self.strategyName}, "oldOrNew"��{self.oldOrNew}, "price": {self.price}, "orderType": {self.orderType}}}"""

    def attach(self, order: 'tqsdk order'):
        """
        ������������order��
        :param order: ���������Ӧ��tqsdk�˻��ĵ����п��ܶ�����ⵥ������ͬһ��tqsdk�ĵ��ϡ�
        :return:
        """
        logger.debug(f'virtual����attach order \n{order}')
        self.realOrder.append(order)

    @property
    def is_dead(self):
        #  ���ȫ����ʵ�������ˣ����ⵥ�Ż���
        for item in self.realOrder:
            logger.debug('��ʵ�����������')
            logger.debug(str(item.is_dead))
            if not item.is_dead:
                self.__is_dead = False
            else:
                self.__is_dead = True
        logger.debug('virtual order ���ն�������ǣ�' + str(self.__is_dead))
        return self.__is_dead

    @is_dead.setter
    def is_dead(self, value):
        self.__is_dead = value

    @property
    def instrument_id(self):
        return self.virContract

    @property
    def insert_date_time(self):
        return self.time

    @property
    def limit_price(self):
        return self.price  # �п����ж����ʵ������ �����޼��µ��ļ�Ǯһ��һ����

    @property
    def direction(self):
        return self.virDirection

    @property
    def contract(self):
        return self.virContract

    @property
    def volumeLeft(self):
        if self.realOrder:
            logger.debug(f'volumeLeft--realOrder:')
            for i in self.realOrder:
                logger.debug(f'{i}')
            if self.realOrder[0].is_online:  # ���������ߵģ��ѵ���������û����
                # is_online �ж�������Ƿ��Ѿ���tqsdk����ȥ�ˣ���Ϊ�����û����ȥ��������ʵ������ʣ�������������ⵥ��ʣ�����Ļ������µ�������
                # �����attach��һ���ֶ�����ûattachȫ����������Ϊ���ⶩ������������
                logger.debug(f'online')
                self.__volumeLeft = 0
                for item in self.realOrder:
                    logger.debug(f'volumeLeft--real volumeLeft:{item.volume_left}')
                    self.__volumeLeft += item.volume_left
            else:
                logger.debug(f'offline')
                if self.is_dead:  # �����ߵ����ˣ�˵��������Ѿ������, ���Ի���Ӧ������ʵ������ʣ��֮��������
                    self.__volumeLeft = 0
                    for item in self.realOrder:
                        logger.debug(f'volumeLeft--real volumeLeft:{item.volume_left}')
                        self.__volumeLeft += item.volume_left

                else:  # ˵���������û��
                    logger.debug(f'unsend')
                    pass
        logger.debug(f'volumeleft:{self.__volumeLeft}')
        return self.__volumeLeft

    @volumeLeft.setter
    def volumeLeft(self, value):
        self.__volumeLeft = value

    @property
    def fee(self):
        """��ȡ��һ����������"""
        # TODO �Ժ�������ô���������ѡ� ��Ҫ�����㷨��
        return 0

    @property
    def filledPrice(self):
        # ��ȡ�ɽ���
        tempVol = 0
        tempSum = 0
        for order in self.realOrder:
            logger.debug(f'filledPrice--realorder')
            for i in self.realOrder:
                logger.debug(f'{i}')
            if not math.isnan(order.trade_price):  # if nan��ΪTrue
                tempSum += order.trade_price * (order.volume_orign - order.volume_left)
                tempVol += (order.volume_orign - order.volume_left)
            else:  # û�гɽ�
                logger.debug(f'filledPrice--unfilled')
                # raise Exception('')
        if tempVol and tempSum:
            assert not math.isnan(tempVol) and not math.isnan(tempSum), f'filledPrice--vol and sum����nanֵ:{tempVol},{tempSum}'
            # ������������Ϊ0���ܳ��������û������ ,����Ҳ��������nan�� if nan �ǻ�����Ϊ�档����bug
            logger.debug(f'filledPrice--vol and sum:{tempVol}, {tempSum}')
            return float(tempSum / tempVol)
        else:
            # assert False, f'�ɽ��۸�Ϊ0��realOrder��{self.realOrder}'
            logger.debug(f'�ɽ��۸�Ϊ0��realOrder��{self.realOrder}')
            return 0

    @property
    def filledVolume(self):
        return self.virVolume - self.volumeLeft

    def getCreatParameter(self):
        """��ȡһ���ܹ����¹������������dict������¼json�ļ�������������δ��ɵĻ�"""
        return {"direction": self.virDirection, "volume": self.volumeLeft, "contract": self.virContract, "open": self.open,
                "strategyName": self.strategyName, "oldOrNew": self.oldOrNew, "price": self.price, "orderType": self.orderType}

    @property
    def is_error(self):
        """����������ʵ�����Ƿ��д�����Ϣ"""
        for real_order in self.realOrder:
            if real_order.is_error:
                return True
        else:
            return False

    @property
    def last_msg(self):
        temp = []
        for real_order in self.realOrder:
            temp.append(real_order.last_msg)
        return temp

class VirtualAlgoOrder(virtualOrder):
    def __init__(self, direction, volume, contract, offset, strategyName, tick, oldOrNew='old', price=None,
                 duration=60, min_volume_each_order=1, max_volume_each_order=100, api=None):
        """
           ��������TqSdk���㷨�� ��TWAP��
           :param tick �µ�Ʒ�ֵ�ʵʱtick ������¼ͳ�Ƴɽ�����
           :param direction:
           :param contract: ��Ĵ��룬���ڱ�׼
           :param volume: ������int
           :param duration: �㷨��������ʱ�䣬float����λ����
           :param min_volume_each_order: (int):������Сί�е���ÿ��ί�е���Ĭ������С�����ֵ�в���
           :param max_volume_each_order:int):�������ί�е���ÿ��ί�е���Ĭ������С�����ֵ�в���
           :param api tqsdk��api
           :return:
       """
        logger.debug(f'��������ʱ��tick�ǣ�{tick}')
        self.tick = tick
        self.duration = duration
        self.min_volume_each_order = min_volume_each_order
        self.max_volume_each_order = max_volume_each_order
        self.twap = Twap(api, contract, direction, offset, volume, duration, self.min_volume_each_order, self.max_volume_each_order)
        super().__init__(direction, volume, contract, True if offset=='OPEN' else False
                         , strategyName, oldOrNew=oldOrNew, price=None, orderType='Algo',tick=tick)

        # highlight ����ͨ��virtualorder ��ͬ��һ�������㷨����ʱֻ��Ӧһ����ʵ�㷨��

        logger.debug(self.__str__())

    def __str__(self):
        return f"""base order info {super().__str__()} algo order info duration: {self.duration}, 
        min_volume_each_order: {self.min_volume_each_order}, 
        max_volume_each_order: {self.max_volume_each_order}"""

    @property
    def is_dead(self):
        return self.twap.is_finished()

    @property
    def limit_price(self):
        return 0  # ��δ������ʹ�õģ���ʱ����0

    @property
    def volumeLeft(self):
        # �㷨��������˾���ȫ�ɣ�û����ɣ�����ʹ�����ʣ��ȥ������
        if self.is_dead:
            return 0
        else:
            return self.virVolume

    @property
    def filledPrice(self):
        # logger.debug(f'�㷨�����ǣ�{self.twap.average_trade_price}')
        # assert not math.isnan(self.twap.average_trade_price), '�㷨ƽ���ɽ���Ϊnan��'
        return self.twap.average_trade_price


class RealFeed(csvfeed.GenericBarFeed):
    """ģ��pyalgotrade ��feed"""

    def __init__(self):
        super().__init__(Frequency.MINUTE)
        self.allDataSource = {}
        self.i = 0
        self.j = 0

    def __getitem__(self, item):
        return self.allDataSource[item]

    def __contains__(self, item):  # �������ж�������ʵ������ in ��ʱ�򴥷����������
        return True if item in self.allDataSource else False

    # iter �� next����ʵ���� for in ���
    def __iter__(self):
        return self

    def __next__(self):
        if self.i < len(self.allDataSource):
            self.i += 1
            return list(self.allDataSource.keys())[self.i - 1]

        self.i = 0  # û��return��Ҫ��i���㡣
        raise StopIteration()

    def __str__(self):
        return str(self.keys())

    def addDataSource(self, sourceName, source):
        self.allDataSource[sourceName] = RealSeries(source)

    def keys(self):
        return self.allDataSource.keys()


class RealSeries:
    """ģ��pyalgotrade ��dataSeries"""

    def __init__(self, source: 'pd.DataFrame'):
        self.data = source

    def __str__(self):
        return str(self.data)

    def getPriceDataSeries(self):
        return self.data['close']

    def getOpenDataSeries(self):
        return self.data['open']

    def getLowDataSeries(self):
        return self.data['low']

    def getHighDataSeries(self):
        return self.data['high']

    def getCloseDataSeries(self):
        return self.data['close']

    def getVolumeDataSeries(self):
        return self.data['volume']

    def getOpen_oiDataSeries(self):
        return self.data['open_oi']


class RealBars:
    """ģ��onBars��Bars"""

    def __init__(self):
        self.__data = {}

    def getInstruments(self):
        return self.__data.keys()

    def getDateTime(self):
        return self.getBar(list(self.getInstruments())[0]).getDateTime()

    def getBar(self, instrument):
        return self.__data[instrument]

    def setValue(self, value: 'dict of pd.Series'):
        for item in value:
            self.__data[item] = RealBar()
            self.__data[item].set(value[item])


class RealBar:

    def __init__(self):
        self.__data = pd.Series()

    def getClose(self):
        return self.__data['close']

    def getDateTime(self):
        return self.__data['datetime']

    def set(self, value: 'pd.Series'):
        self.__data = value
